package org.spbsu.apco.server.taskset;

import org.spbsu.apco.common.task.core.BaseTask;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * User: solozobov
 * Date: 13.11.12
 */
public abstract class BaseTaskSet {

    private static final AtomicLong NEXT_TASK_SET_ID = new AtomicLong(0);

    private final long id;
    private final Set<BaseTask> tasks = new HashSet<BaseTask>();
    private final Map<BaseTask, List<?>> task2input = new HashMap<BaseTask, List<?>>();
    private boolean tasksInitialized = false;

    protected BaseTaskSet() {
        id = NEXT_TASK_SET_ID.incrementAndGet();
    }

    /**
     * @see {addLeafTask} for addition of independent tasks
     * @see {addTask} for addition of tasks with other tasks results as arguments
     */
    public abstract void initializeTasks();

    public Set<BaseTask> getTasks() {
        tryToInitialize();
        return tasks;
    }

    public Map<BaseTask, List<?>> getTask2input() {
        tryToInitialize();
        return task2input;
    }

    public final long getId() {
        return id;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        BaseTaskSet that = (BaseTaskSet) o;

        if (id != that.id) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return (int) (id ^ (id >>> 32));
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("BaseTaskSet{").
                append("tasks={");

        for (BaseTask task : tasks) {
            sb.append(task.getClass().getSimpleName()).append("#").append(task.getId()).append(",");
        }
        sb      .append("}").append(", inputData=").append(task2input)
                .append(", tasksInitialized=").append(tasksInitialized)
                .append("}");
        return sb.toString();
    }

    protected void addTask(final BaseTask task) {
        if (task.getSubTasks().isEmpty()) {
            throw new RuntimeException("Not leaf task must have subtasks");
        }
        tasks.add(task);
    }

    protected <T> void addLeafTask(final BaseTask<T, ?> task, final List<T> input) {
        if (task2input.containsKey(task)) {
            throw new RuntimeException("Task is already added as not leaf task");
        }
        if (!task.getSubTasks().isEmpty()) {
            throw new RuntimeException("Leaf task can't have subtasks");
        }
        task2input.put(task, input);
        tasks.add(task);
    }

    private void tryToInitialize() {
        if (!tasksInitialized) {
            initializeTasks();
            tasksInitialized = true;
        }
    }
}
